fix(json): only convert unambiguous date/datetime/UUID strings in additional data#664
Conversation
…itional data try_get_anything ran every untyped string through five lenient parsers (datetime, UUID, timedelta, date, time). On Python 3.11+ this mangled ordinary text values landing in additional_data: "19-2026" became time(19, 0) at offset -20:26, "PT" became timedelta(0), "11.0" became time(11, 0), and 32-char hex strings became UUIDs. Round-tripping such records then persisted the mangled strings, corrupting user data. Only convert strings matching an anchored full ISO date/datetime shape or the canonical hyphenated UUID form, mirroring the .NET JsonParseNode which attempts strict DateTime/DateTimeOffset/Guid parses only. Date-only strings still parse through datetime_from_iso_format_compat, preserving the existing datetime result for real payload values. The previous four-digit and isdigit special cases are no longer needed since all-digit strings cannot match either pattern. Fixes microsoft#487 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Uui4g7E7UmzKRGHMW5zbH
|
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
This PR tightens JsonParseNode.try_get_anything so that untyped strings routed into additional_data are only coerced when they are unambiguously ISO date/datetime literals or canonical hyphenated UUIDs, preventing accidental corruption of ordinary text values (e.g., invoice numbers, country codes) on round-trip updates.
Changes:
- Replaces speculative “try a bunch of parsers” logic with two anchored regex gates (ISO date/datetime prefix and canonical UUID) before attempting conversion.
- Removes the implicit
timedelta/timeguessing behavior for untyped strings inadditional_data. - Adds unit tests covering previously-corrupted text patterns and pins the intended conversions (date-only →
datetime, canonical UUID →UUID).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/serialization/json/kiota_serialization_json/json_parse_node.py | Restricts try_get_anything string coercions to unambiguous ISO date/datetime and canonical UUID shapes. |
| packages/serialization/json/tests/unit/test_json_parse_node.py | Adds parameterized coverage to ensure common text values are not coerced, and validates the remaining intended conversions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
|
|
||
| def test_get_anything_converts_date_only_string_to_datetime(): | ||
| parse_node = JsonParseNode("2023-10-05") |
There was a problem hiding this comment.
Can we have variations with time, and time + offset to prevent any further regression?



Overview
JsonParseNode.try_get_anythingruns every untyped string (everything landing inadditional_data) through five lenient parsers in sequence:datetime_from_iso_format_compat,UUID,parse_timedelta_string,date.fromisoformat,time_from_iso_format_compat. On Python 3.11+ the lenient parsers accept many ordinary text values:This is not just a display problem: in our production SharePoint AP-invoicing app, invoice numbers like
19-2026were coerced totimeobjects, later stringified on update, and persisted back — permanently corrupting stored data (details with a captured wire payload in microsoftgraph/msgraph-sdk-python#1340).This PR replaces the speculative parsing with two anchored patterns, mirroring the .NET
JsonParseNode.TryGetAnything, which only attempts strictDateTime/DateTimeOffset/Guidparses and otherwise returns the string:^\d{4}-\d{2}-\d{2}([Tt ]\d{2}:\d{2}|$)) →datetime_from_iso_format_compatUUIDBehavior is unchanged for real payload values: full ISO datetimes still become
datetime, date-only strings still becomedatetime(same as today, via the same compat parser), canonical UUIDs still becomeUUID. Thetimedelta/timeguessing is removed entirely — Graph does not send bare durations or times as untyped strings, and their grammars overlap with too much real-world text. The__is_four_digit_number/isdigit()special cases (added for earlier symptom reports like microsoftgraph/msgraph-sdk-python#653) become unnecessary since all-digit strings cannot match either anchored pattern, and are removed.Related Issue
Fixes #487
Addresses microsoftgraph/msgraph-sdk-python#1340 and microsoftgraph/msgraph-sdk-python#653
Notes
timedelta/time/unhyphenated-UUID will now stay strings inadditional_data. That is the intent of the fix, but callers who relied on those accidental conversions would see a change; typed model fields are unaffected (they go through declared field deserializers and never reachtry_get_anything).Created/Modifieddatetimes still parse.Testing Instructions
cd packages/serialization/json && pytest tests/test_get_anything_does_not_coerce_text_valuescovers each reported corruption pattern (19-2026,PT,10:30,23.085,11.0, 32-hex, date fragments)datetime, canonical UUID →UUID; existing tests cover full ISO datetimes🤖 Generated with Claude Code
https://claude.ai/code/session_019Uui4g7E7UmzKRGHMW5zbH